﻿using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Timers;
using System.Windows.Forms;
using ConfigLibrary;
using Contacts.Mail;
using Contacts.Net;
using NetConnector.Net.Client;

namespace Contacts
{
    //[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    public partial class Contacts : Form
    {
        #region Private Declarations
        /// <summary>
        /// Background worker for the timer threads
        /// </summary>
        private BackgroundWorker bgw;
        private System.Timers.Timer tm;
        private int _time;

        /// <summary>
        /// Global flag to set when users change data.
        /// </summary>
        private bool _isdirty;
        private watcher watch;
        private Config config;
        private Client client;

        #endregion

        #region Ctor
        /// <summary>
        /// This constructor starts the form. First it trys to use TCP clients with a server to get notices when data changes.  
        /// Failing that it will try the folder watcher methods.
        /// Failing that it starts a timer to prompt the user to manually check the data.
        /// </summary>
        /// <param name="time">Integer of time. The integer represents time in Minutes</param>
        public Contacts(int time)
        {
            try
            {
                // Designer code
                InitializeComponent();

                //loads the config file
                Stream s = File.Open(Properties.Settings.Default.CONFIGPATH, FileMode.Open);
                BinaryFormatter b = new BinaryFormatter();
                config = (Config)b.Deserialize(s);
                s.Close();
                //end load

                // Save the time value globally for later use
                _time = time;
                this.bgw = new BackgroundWorker();
                // Hook the DoWork event.  Event is raised when RunAsync is called. 
                this.bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
                try
                {
                    //client = new Client(config.ServerPort, config.ClientPort, config.DataHost);
                    //client.Start();
                    //client.DataChanged += new Client.DataChangeHandler(client_DataChanged);
                }
                catch (Exception ex)
                {
                    mailman.sendmail("Client or Server Error", "Couldn't get the client going." + Environment.NewLine + "Call Stack: " + ex.StackTrace + "\n Error Message: "
                        + ex.Message + Environment.NewLine + "Trying File Watcher events.");
                    try
                    {
                        watch = new watcher(config.MonitorFolderPath);
                        watch.Start();
                        GC.KeepAlive(watch);
                        watch.WatchedDataChange += new watcher.OnWatchedDataChange(watch_WatchedDataChange);
                    }
                    catch (Exception er)
                    {
                        mailman.sendmail("File Watcher Error", "Couldn't get the file watcher going." + Environment.NewLine + "Stack Trace: " +
                        er.StackTrace + "\r\n Error Message: " + er.Message + Environment.NewLine + "Switching to timed message events.");
                        bgw.RunWorkerAsync(_time);
                    }
                }

            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }
        #endregion

        #region Events
        void watch_WatchedDataChange(object sender, FileSystemEventArgs e)
        {
            if (_isdirty)
            {
                MessageBox.Show("Some one saved new data!  \"Ok\" to load it but you might lose your unsaved work.  \"Cancel\" is obvious", "Potential Loss of Data!", MessageBoxButtons.OKCancel);
            }
            else
            {
                Contacts_Load(sender, e);
            }
        }

        /// <summary>
        /// Use to respond to events when some one has change the MySql data
        /// </summary>
        /// <param name="args">Should have the command "Reload" among other values</param>
        void client_DataChanged(NetConnector.Net.DataArguments args)
        {
            if (_isdirty)
            {
                MessageBox.Show("Some one saved new data!  \"Ok\" to load it but you might lose your unsaved work.  \"Cancel\" is obvious", "Potential Loss of Data!", MessageBoxButtons.OKCancel);
            }
            else
            {
                Contacts_Load(this, new EventArgs());
                MessageBox.Show("I reloaded your data.  \"Cancel\" is obvious", "reloaded", MessageBoxButtons.OKCancel);
            }
        }



        /// <summary>
        /// DoWork delegate for the background worker.  This creates a timer and hooks timer events.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bgw_DoWork(object sender, DoWorkEventArgs e)
        {
            /* Uncomment the next line if you need to do something
             * with the background worker that started this thread
             * i.e. check on progress
             */
            //BackgroundWorker bee = (BackgroundWorker)sender;
            // Timers set the count in milliseconds.  The integer we sent was to be minutes
            // so some math needs to be done to convert.
            // (1) millsecond = 1/1000; 1ms x 1000 = 1 sec; 1s x 60s = 1 min
            // therefore 1ms x 60000 = 1 min
            tm = new System.Timers.Timer((int)e.Argument * 60000);
            tm.Elapsed += new ElapsedEventHandler(tm_Elapsed);
            // set enabled true to start the timer
            tm.Enabled = true;
            // the next line is needed to prevent garbage collection
            GC.KeepAlive(tm);
        }

        /// <summary>
        /// Event is raised when the timer's interval runs out.
        /// This is also raised on a different thread so we don't have access to object created on the original caller's thread
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void tm_Elapsed(object sender, ElapsedEventArgs e)
        {
            /* Need to somhow check if the current data is still good
             * or if its "dirty"
             * "dirty" means its been changed since it was last updated from the server
             * "dirty" can either be because the user changed it
             * or someone else changed it.  For now just use msgbox to let
             * the user know that they need to reload the data
             */

            DialogResult result = MessageBox.Show("It's been " + Properties.Settings.Default.TIME_ALIVE + " minutes since you loaded data" + "\r\n" + "Which means you should Reload since the data may have changed." +
                "\r\n" + "\"OK\" to reload now. \"Cancel\" not ignore for a bit.", "Time's Up!", MessageBoxButtons.OKCancel);
            switch (result)
            {
                case DialogResult.OK:
                    {
                        // need to call this back on the main thread.
                        // Invoke 
                        //Contacts_Load(this, new EventArgs());

                        break;
                    }
                case DialogResult.Cancel:
                    {
                        return;
                    }
            }
        }
        #endregion
       
    }
}